type bstmt =
    Int of int    (* add an int to the heap *)
  | Str of string (* add a string to the heap *)
  | Arr of int * int   (* array length, is empty *)
  | Drp           (* Discard a value *)
  | Opr of Ast.op * Ast.typ * int (* Perform all operations *)
  | Lod of int    (* Fetch local variable *)
  | Sto of int    (* Store local variable *)
  | Jsr of int    (* Call function by absolute address *)
  | Ent of int    (* Push FP, FP -> SP, SP += i *)
  | Ret of int    (* Restore FP, SP, consume formals, push result *)
  | Beq of int    (* Branch relative if top-of-stack is zero *)
  | Bne of int    (* Branch relative if top-of-stack is non-zero *)
  | Bra of int    (* Branch relative *)
  | Hlt           (* Terminate *)

type prog = {
    text : bstmt array; (* Code for all the functions *)
  }
  
let string_of_type = function
	Ast.Int -> "I"
	| Ast.Arr -> "A"
	| Ast.Str -> "S"
	| Ast.Any -> "NE"

let string_of_stmt = function
    Int(i) -> "Int " ^ string_of_int i
  | Str(s) ->  "Str \"" ^ s ^ "\""
  | Arr(l, f) -> "Arr " ^ string_of_int l ^ " " ^ string_of_int f
  | Drp -> "Drp"
  | Opr(Ast.Add, t, a) -> "Add " ^ string_of_type t ^ " " ^ string_of_int a
  | Opr(Ast.Sub, t, a) -> "Sub " ^ string_of_type t ^ " " ^ string_of_int a
  | Opr(Ast.Eq, t, a) -> "Eql " ^ string_of_type t ^ " " ^ string_of_int a
  | Opr(Ast.Neq, t, a) -> "Neq " ^ string_of_type t ^ " " ^ string_of_int a
  | Opr(Ast.Lt, t, a) -> "Lt " ^ string_of_type t ^ " " ^ string_of_int a
  | Opr(Ast.Leq, t, a) -> "Leq " ^ string_of_type t ^ " " ^ string_of_int a
  | Opr(Ast.Gt, t, a) -> "Gt " ^ string_of_type t ^ " " ^ string_of_int a
  | Opr(Ast.Geq, t, a) -> "Geq " ^ string_of_type t ^ " " ^ string_of_int a
  | Opr(Ast.And, t, a) -> "And " ^ string_of_type t ^ " " ^ string_of_int a
  | Opr(Ast.Or, t, a) -> "Or " ^ string_of_type t ^ " " ^ string_of_int a
  | Opr(Ast.Vat, t, a) -> "Vat " ^ string_of_type t ^ " " ^ string_of_int a
  | Opr(Ast.Not, t, a) -> "Not " ^ string_of_type t ^ " " ^ string_of_int a
  | Opr(Ast.In, t, a) -> "In " ^ string_of_type t ^ " " ^ string_of_int a
  | Opr(Ast.Len, t, a) -> "Len " ^ string_of_type t ^ " " ^ string_of_int a
  | Opr(Ast.Out, t, a) -> "Out " ^ string_of_type t ^ " " ^ string_of_int a
  | Opr(Ast.Rng, t, a) -> "Rng " ^ string_of_type t ^ " " ^ string_of_int a 
  | Opr(Ast.Sai, t, a) -> "Sai " ^ string_of_type t ^ " " ^ string_of_int a 
  | Lod(i) -> "Lod " ^ string_of_int i
  | Sto(i) -> "Sto " ^ string_of_int i
  | Jsr(i) -> "Jsr " ^ string_of_int i
  | Ent(i) -> "Ent " ^ string_of_int i
  | Ret(i) -> "Ret " ^ string_of_int i
  | Bne(i) -> "Bne " ^ string_of_int i
  | Beq(i) -> "Beq " ^ string_of_int i
  | Bra(i) -> "Bra " ^ string_of_int i
  | Hlt    -> "Hlt"

let string_of_prog p =
  let funca = Array.mapi
      (fun i s -> string_of_int i ^ " " ^ string_of_stmt s) p.text
  in String.concat "\n" (Array.to_list funca)
